﻿// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动！任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任！

using Admin.NET.Application.Entity;
using Admin.NET.Application.Request;
using Admin.NET.Core.Service;
using DocumentFormat.OpenXml.Wordprocessing;
using Elastic.Clients.Elasticsearch.Snapshot;
using Furion.DependencyInjection;
using Furion.FriendlyException;
using Mapster;
using Markdig.Helpers;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Options;
using System.IO;
using Yitter.IdGenerator;

namespace Admin.NET.Application.Service;

/// <summary>
/// 租房实体接口
/// </summary>
[ApiDescriptionSettings(ApplicationConst.GroupName, Name = "Rentout", Order = 200)]
public class RentoutService : IDynamicApiController {

    private readonly UserManager _userManager;

    /// <summary>
    /// 数据库仓储接口
    /// </summary>
    private readonly ISqlSugarRepository<RentoutEntity> _repository;

    private readonly UploadOptions _uploadOptions;
    private readonly OSSProviderOptions _OSSProviderOptions;
    private readonly SqlSugarRepository<SysFile> _sysFileRep;
    private readonly ICustomFileProvider _customFileProvider;
    private HttpContext _httpContext;

    public RentoutService(UserManager userManager,
         IOptions<UploadOptions> uploadOptions,
        SqlSugarRepository<SysFile> sysFileRep,
        IOptions<OSSProviderOptions> oSSProviderOptions,
        IHttpContextAccessor httpContextAccessor,
         INamedServiceProvider<ICustomFileProvider> namedServiceProvider,
        SqlSugarRepository<RentoutEntity> repository) {
        _userManager = userManager;
        _repository = repository;
        _sysFileRep = sysFileRep;
        _OSSProviderOptions = oSSProviderOptions.Value;
        _uploadOptions = uploadOptions.Value;
        _httpContext = httpContextAccessor.HttpContext;
        _customFileProvider = namedServiceProvider.GetService<ITransient>(nameof(DefaultFileProvider));
    }


    #region 1.0 分页查询数据 +Task<SqlSugarPagedList<RentoutEntity>> GetList([FromQuery] ListShopInput input)
    [HttpGet]
    [AllowAnonymous]
    public async Task<SqlSugarPagedList<RentoutEntity>> GetList([FromQuery] ListRentoutInput input) {

        var remoteIPv4 = _httpContext.GetRemoteIpAddressToIPv4();
        // 判断是否为本地IP
        (string ipLocation, double? longitude, double? latitude) = CommonUtil.GetIpAddress(remoteIPv4);
        List<String> ranges = App.GetConfig<List<String>>("IpLocaiton", true);

        Console.WriteLine("remoteIPv4={0},addr={1}", remoteIPv4, ipLocation);
        if (!ranges.Contains(ipLocation)) {
            return new SqlSugarPagedList<RentoutEntity>();
        }


        SqlSugarPagedList<RentoutEntity> list = await _repository.AsQueryable()
             .WhereIF(!String.IsNullOrWhiteSpace(input.Keyword), it => it.Title.Contains(input.Keyword)
                || it.Address.Contains(input.Keyword)
                || it.PhoneNumbers.Contains(input.Keyword)
                || it.RentalType.Contains(input.Keyword)
                )
            .OrderByDescending(it => it.CreateTime)
             .ToPagedListAsync(input.Page, input.PageSize);

        // 查询所有租房数据
        return list;
    }
    #endregion

    #region 2.0 主键获取实体 +Task<RentoutEntity> GetById(string id) 
    [HttpGet("{id}")]
    [AllowAnonymous]
    public async Task<RentoutEntity> GetById(string id) {
        RentoutEntity entity = await _repository.GetByIdAsync(long.Parse(id));
        if (entity == null) {
            throw new Exception("租房不存在");
        }

        int viewCount = entity.ViewCount ?? 0;
        _repository.AsUpdateable()
            .SetColumns(it => it.ViewCount == viewCount + 1)
            .Where(it => it.Id == entity.Id)
            .ExecuteCommand();
        return entity;
    }
    #endregion

    #region 3.0 新增实体数据 +Task Create([FromBody] RentoutRequest input) 
    [HttpPost]
    public async Task Create([FromBody] RentoutRequest input) {
        if (input == null) {
            throw Oops.Oh("请求数据不能为空");
        }
        RentoutEntity entity = input.Adapt<RentoutEntity>();
        entity.Id = SnowFlakeSingle.Instance.NextId();
        entity.CreateUserId = _userManager.UserId;
        entity.CreateUserName = _userManager.Account;
        await _repository.Context.Insertable(entity)
            .CallEntityMethod(it => it.Creater()).ExecuteCommandAsync();
    }
    #endregion

    #region 3.1 发布实体数据 +Task UserPublish([FromBody] RentoutRequest input) 
    [AllowAnonymous]
    [HttpPost("publish")]
    public async Task UserPublish([FromBody] RentoutRequest input) {
        if (input == null) {
            throw Oops.Oh("请求数据不能为空");
        }
        RentoutEntity entity = input.Adapt<RentoutEntity>();
        entity.CreateUserName = "publish";
        entity.CreateTime = DateTime.Now;
        entity.Status = 100;

        await _repository.Context.Insertable(entity)
            .CallEntityMethod(it => it.Creater())
            .ExecuteCommandAsync();
    }
    #endregion

    #region 4.0 移除主键的数据 +Task Delete(string id) 
    [HttpDelete("{id}")]
    public async Task Delete(string id) {
        if (string.IsNullOrWhiteSpace(id)) {
            throw Oops.Oh("ID不能为空");
        }
        // 查询所有租房数据
        await _repository.DeleteByIdAsync(long.Parse(id));
    }
    #endregion

    #region 5.0 修改实体数据 +Task Update([FromBody] RentoutRequest input)
    [HttpPut]
    public async Task Update([FromBody] RentoutRequest input) {
        if (input == null) {
            throw Oops.Oh("请求数据不能为空");
        }
        if (string.IsNullOrWhiteSpace(input.Id)) {
            throw Oops.Oh("ID不能为空");
        }
        RentoutEntity entity = input.Adapt<RentoutEntity>();
        entity.UpdateTime = DateTime.Now;
        entity.UpdateUserId = _userManager.UserId;
        // 更新租房数据 
        if (!await _repository.UpdateAsync(entity)) {
            throw Oops.Oh("更新租房数据失败");
        }

    }
    #endregion

    #region 6.0 图片上传 +Task<SysFile>  UploadFile([FromForm] UploadFileInput input,  [BindNever] string targetPath = "")
    [AllowAnonymous]
    public async Task<Object> UploadFile([FromForm] UploadFileInput input, [BindNever] string targetPath = "") {

        if (input.File == null || input.File.Length <= 0) throw Oops.Oh(ErrorCodeEnum.D8000);

        if (input.File.FileName.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) throw Oops.Oh(ErrorCodeEnum.D8005);

        // 验证文件类型
        if (!_uploadOptions.ContentType.Contains(input.File.ContentType)) throw Oops.Oh($"{ErrorCodeEnum.D8001}:{input.File.ContentType}");

        var sizeKb = input.File.Length / 1024; // 大小KB
        // 验证文件大小
        if (sizeKb > _uploadOptions.MaxSize) throw Oops.Oh($"{ErrorCodeEnum.D8002}，允许最大：{_uploadOptions.MaxSize}KB");
        // 获取文件后缀
        var suffix = Path.GetExtension(input.File.FileName).ToLower(); // 后缀
        if (string.IsNullOrWhiteSpace(suffix))
            suffix = string.Concat(".", input.File.ContentType.AsSpan(input.File.ContentType.LastIndexOf('/') + 1));
        if (!string.IsNullOrWhiteSpace(suffix)) {
            //var contentTypeProvider = FS.GetFileExtensionContentTypeProvider();
            //suffix = contentTypeProvider.Mappings.FirstOrDefault(u => u.Value == file.ContentType).Key;
            // 修改 image/jpeg 类型返回的 .jpeg、jpe 后缀
            if (suffix == ".jpeg" || suffix == ".jpe")
                suffix = ".jpg";
        }
        if (string.IsNullOrWhiteSpace(suffix)) throw Oops.Oh(ErrorCodeEnum.D8003);

        // 防止客户端伪造文件类型
        if (!string.IsNullOrWhiteSpace(input.AllowSuffix) && !input.AllowSuffix.Contains(suffix)) throw Oops.Oh(ErrorCodeEnum.D8003);

        // 文件存储位置
        var path = string.IsNullOrWhiteSpace(targetPath) ? _uploadOptions.Path : targetPath;
        path = path.ParseToDateTimeForRep();

        var newFile = input.Adapt<SysFile>();
        newFile.Id = YitIdHelper.NextId();
        newFile.BucketName = _OSSProviderOptions.Enabled ? _OSSProviderOptions.Bucket : "Local"; // 阿里云对bucket名称有要求，1.只能包括小写字母，数字，短横线（-）2.必须以小写字母或者数字开头  3.长度必须在3-63字节之间
        newFile.FileName = Path.GetFileNameWithoutExtension(input.File.FileName);
        newFile.Suffix = suffix;
        newFile.SizeKb = sizeKb;
        newFile.FilePath = path;
        newFile.FileMd5 = string.Empty;

        var finalName = newFile.Id + suffix; // 文件最终名称

        newFile = await _customFileProvider.UploadFileAsync(input.File, newFile, path, finalName);
        await _sysFileRep.AsInsertable(newFile).ExecuteCommandAsync();
        return new { url = newFile.Url, fileName = newFile.FileName, id = newFile.Id };
    }
    #endregion
}